package coalition.formation;


import java.util.ArrayList;
import java.util.List;



import coalition.formation.coalitions.Core;
import coalition.formation.coalitions.MergeProof;
import coalition.formation.coalitions.NoTreat;
import coalition.formation.coalitions.SplitProof;
import coalition.formation.externality.Externality;
import coalition.formation.partitions.PartitionDecorator;


public class Population implements PopulationInterface {

	int[] dimensions;
	UtilityProvider utilityProvider;
	Externality[] oneDimensionalExternalities;
	
	public static PopulationInterface getPopulationInstance(int[] dimensions, UtilityProvider utilityProvider){
		return new Population(dimensions, utilityProvider);
	}
	
	public Population(int[] dimensions, UtilityProvider utilityProvider){
		this.dimensions = dimensions;
		this.utilityProvider = utilityProvider;
		if(!intArraysEqual(utilityProvider.getDimensions(), dimensions)){
			throw new IllegalArgumentException("utilityProvider incompatible with population dimensions");
		}
		this.oneDimensionalExternalities = new ExternalityCache[dimensions.length];
		for(int i = 0; i < oneDimensionalExternalities.length; i++){
			MultiDimensionalExternalityWrapper wrapper = new MultiDimensionalExternalityWrapper(utilityProvider);
			//set the determining attribute for this multiDimensionalExternalityWrapper
			wrapper.setAttributeIndex(i);
			ExternalityCache externalityCache = new ExternalityCache(wrapper);
			oneDimensionalExternalities[i] = externalityCache;
			
		}
		//new MultiDimensionalExternalityWrapper(utilityProvider);
	}
	
	public List<int[]> getCore(int attributeIndex){
		//multiDimensionalWrapper.setAttributeIndex(attributeIndex);
		Core core = new Core(oneDimensionalExternalities[attributeIndex]);
		List<int[]> corePartitions = core.getCore();
		return corePartitions;
	}
	
	public List<int[]> getSplitProofPartitions(int attributeIndex){
		//multiDimensionalWrapper.setAttributeIndex(attributeIndex);
		SplitProof splitProof = new SplitProof(oneDimensionalExternalities[attributeIndex]);
		List<int[]> splitProofPartitions = splitProof.getSplitProofPartitions();
		return splitProofPartitions;
	}
	
	public List<int[]> getMergeProofPartitions(int attributeIndex){
		//multiDimensionalWrapper.setAttributeIndex(attributeIndex);
		MergeProof mergeProof = new MergeProof(oneDimensionalExternalities[attributeIndex]);
		List<int[]> mergeProofPartitions = mergeProof.getMergeProofPartitions();
		return mergeProofPartitions;
	}	

	public List<int[]> getNoTreatPartitions(int attributeIndex){
		//multiDimensionalWrapper.setAttributeIndex(attributeIndex);
		NoTreat noTreat = new NoTreat(oneDimensionalExternalities[attributeIndex]);
		List<int[]> noTreatPartitions = noTreat.getNoTreatPartitions();
		return noTreatPartitions;
	}		
	
	
	boolean intArraysEqual(int[] a, int[] b){
		if(a.length != b.length)
			throw new IllegalArgumentException("array must have equal dimensions");
		for(int i = 0; i < a.length; i++)
			if(a[i] != b[i]) return false;
		return true;
	}

	@Override
	public List<List<int[]>> getCoreInListForm(int attributeIndex) {
		List<List<int[]>> result = new ArrayList<List<int[]>>();
		List<int[]> partitions = getCore(attributeIndex);
		for(int[] partition : partitions){
			List<int[]> coalition = PartitionDecorator.getCoalitionLists(partition);
			result.add(coalition);
		}
		return result;
	}

	@Override
	public List<List<int[]>> getMergeProofPartitionsInListForm(int attributeIndex) {
			List<List<int[]>> result = new ArrayList<List<int[]>>();
			List<int[]> partitions = getMergeProofPartitions(attributeIndex);
			for(int[] partition : partitions){
				List<int[]> coalition = PartitionDecorator.getCoalitionLists(partition);
				result.add(coalition);
			}
			return result;
	}

	@Override
	public List<List<int[]>> getNoTreatPartitionsInListForm(int attributeIndex) {
		List<List<int[]>> result = new ArrayList<List<int[]>>();
		List<int[]> partitions = getNoTreatPartitions(attributeIndex);
		for(int[] partition : partitions){
			List<int[]> coalition = PartitionDecorator.getCoalitionLists(partition);
			result.add(coalition);
		}
		return result;
	}

	@Override
	public List<List<int[]>> getSplitProofPartitionsInListForm(int attributeIndex) {
			List<List<int[]>> result = new ArrayList<List<int[]>>();
			List<int[]> partitions = getSplitProofPartitions(attributeIndex);
			for(int[] partition : partitions){
				List<int[]> coalition = PartitionDecorator.getCoalitionLists(partition);
				result.add(coalition);
			}
			return result;
	}

	@Override
	public List<List<List<int[]>>> getCoreByCrossSection(int attributeIndex) {
		List<int[]> partitions = getCore(attributeIndex);
		List<List<List<int[]>>> result = convertToPartitionsByCrossSection(
				attributeIndex, partitions);
		return result;
	}

	@Override
	public List<List<List<int[]>>> getMergeProofPartitionsByCrossSection(
			int attributeIndex) {
		List<int[]> partitions = getMergeProofPartitions(attributeIndex);
		List<List<List<int[]>>> result = convertToPartitionsByCrossSection(
				attributeIndex, partitions);
		return result;
	}

	@Override
	public List<List<List<int[]>>> getNoTreatPartitionsByCrossSection(
			int attributeIndex) {
		List<int[]> partitions = getNoTreatPartitions(attributeIndex);
		List<List<List<int[]>>> result = convertToPartitionsByCrossSection(
				attributeIndex, partitions);
		return result;
	}

	@Override
	public List<List<List<int[]>>> getSplitProofPartitionsByCrossSection(
			int attributeIndex) {
		List<int[]> partitions = getSplitProofPartitions(attributeIndex);
		List<List<List<int[]>>> result = convertToPartitionsByCrossSection(
				attributeIndex, partitions);
		return result;
	}

	@Override
	public List<int[]> getMergeProofSplitProofPartitions(int attributeIndex) {
		MergeProof mergeProof = new MergeProof(oneDimensionalExternalities[attributeIndex]);
		List<int[]> mergeProofPartitions = mergeProof.getMergeProofPartitions();
		SplitProof splitProof = new SplitProof(oneDimensionalExternalities[attributeIndex]);
		List<int[]> mergeProofSplitProofPartitions = splitProof.getSplitProofPartitions(mergeProofPartitions);		
		return mergeProofSplitProofPartitions;
	}

	private List<List<List<int[]>>> convertToPartitionsByCrossSection(
			int attributeIndex, List<int[]> partitions){
		List<List<List<int[]>>> result = new ArrayList<List<List<int[]>>>();
		MultiDimensionalExternalityWrapper wrapper = 
			new MultiDimensionalExternalityWrapper(utilityProvider);
		wrapper.setAttributeIndex(attributeIndex);
		for(int[] partition : partitions){
			//List<int[]> coalition = PartitionDecorator.getCoalitionLists(partition);
			List<List<int[]>> partitionByCrossSections = 
				wrapper.getPartitionByCrossSections(partition);
			result.add(partitionByCrossSections);
		}
		return result;
	}
	
	@Override
	public List<List<List<int[]>>> getMergeProofSplitProofPartitionsByCrossSection(
			int attributeIndex) {
		List<int[]> partitions = getMergeProofSplitProofPartitions(attributeIndex);
		List<List<List<int[]>>> result = convertToPartitionsByCrossSection(
				attributeIndex, partitions);
		return result;
	}

	@Override
	public List<List<int[]>> getMergeProofSplitProofPartitionsInListForm(
			int attributeIndex) {
		List<List<int[]>> result = new ArrayList<List<int[]>>();
		List<int[]> partitions = getMergeProofSplitProofPartitions(attributeIndex);
		for(int[] partition : partitions){
			List<int[]> coalition = PartitionDecorator.getCoalitionLists(partition);
			result.add(coalition);
		}
		return result;
	}


	
}

